package main

import (
	"fmt"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/antlinker/alog"
)

// TotalElem 统计元素
type TotalElem struct {
	Cnt          int64
	Precnt       int64
	Frequency    float64
	MaxFrequency float64
	Agvcnt       float64
}

func (t *TotalElem) add() {
	atomic.AddInt64(&t.Cnt, 1)
	atomic.AddInt64(&t.Precnt, 1)
}
func (t *TotalElem) addN(n int64) {
	atomic.AddInt64(&t.Cnt, n)
	atomic.AddInt64(&t.Precnt, n)
}

// Claculation 计算
func (t *TotalElem) claculation(sp float64, keeptime float64) {
	precnt := t.Precnt
	//计算实时流量
	frequency := float64(precnt) / sp
	t.Frequency = frequency
	atomic.AddInt64(&t.Precnt, -precnt)
	if t.MaxFrequency < t.Frequency {
		t.MaxFrequency = t.Frequency
	}
	t.Agvcnt = float64(t.Cnt) / keeptime

}
func createTotaler(isShow bool) Totaler {
	t := &cliTotal{isShow: isShow}
	return t
}

// Totaler 统计器
type Totaler interface {
	AddEvent()
}
type cliTotal struct {
	ClientID  string
	StartTime time.Time
	CurTime   time.Time
	RecvEvent TotalElem
	isShow    bool
	lck       sync.Mutex
}

// Claculation 计算
func (t *cliTotal) claculation() {
	t.lck.Lock()
	defer t.lck.Unlock()
	now := time.Now()
	kt := now.Sub(t.StartTime).Seconds()
	sp := now.Sub(t.CurTime).Seconds()
	if sp >= 1 {
		t.RecvEvent.claculation(sp, kt)
		t.CurTime = now
		if t.isShow {
			t.show()
		}
	}
}
func (t *cliTotal) show() {
	ct := t
	alog.Info("\rtime:", time.Now(), "\n", line("")+
		line("项目", "总数量", "次数/秒", "最大/秒", "平均／秒")+
		creLine1("接收事件", ct.RecvEvent))

}
func creLine1(name string, e TotalElem) string {
	return line(name, fmtint(e.Cnt), fmtfloat(e.Frequency), fmtfloat(e.MaxFrequency), fmtfloat(e.Agvcnt))
}

// func creLine2(name string, e TotalElem) string {
// 	return line(name, fmtFloatFlow(float64(e.Cnt)), fmtFloatFlow(e.Frequency), fmtFloatFlow(e.MaxFrequency), fmtFloatFlow(e.Agvcnt))
// }
func (t *cliTotal) AddEvent() {
	t.RecvEvent.add()
	t.claculation()
}

func line(cols ...string) string {
	var out = ""
	for _, col := range cols {
		out += fmlenstr(col, 15)
	}
	return out + "\n"
}
func fmlenstr(str string, slen int) string {
	clen := bjlen(str)
	if slen > clen {
		return str + strings.Repeat(" ", slen-clen)
	}
	return str

}
func bjlen(s string) int {
	return (len([]rune(s)) + len(s)) / 2
}
func fmtint(num int64) string {
	return strconv.FormatInt(num, 10)
}
func fmtfloat(num float64) string {
	return strconv.FormatFloat(num, 'f', 3, 64)
}

const (
	flowk uint64 = 2 << (10*(iota+1) - 1)
	flowm
	flowg
	flowt
	flowp
	flowe
)

// func fmtFlow(num uint64) string {
// 	switch {
// 	case num >= flowe:
// 		return fmt.Sprintf("%.2f", float64(num)/float64(flowe)) + "EB"
// 	case num >= flowp:
// 		return fmt.Sprintf("%.2f", float64(num)/float64(flowp)) + "PB"
// 	case num >= flowt:
// 		return fmt.Sprintf("%.2f", float64(num)/float64(flowt)) + "TB"
// 	case num >= flowg:
// 		return fmt.Sprintf("%.2f", float64(num)/float64(flowg)) + "GB"
// 	case num >= flowm:
// 		return fmt.Sprintf("%.2f", float64(num)/float64(flowm)) + "MB"
// 	case num >= flowk:
// 		return fmt.Sprintf("%.2f", float64(num)/float64(flowk)) + "KB"
// 	default:
// 		return fmt.Sprintf("%d", num) + "B"
// 	}

// }
func fmtFloatFlow(num float64) string {
	switch {
	case num >= float64(flowe):
		return fmt.Sprintf("%.2f", float64(num)/float64(flowe)) + "EB"
	case num >= float64(flowp):
		return fmt.Sprintf("%.2f", float64(num)/float64(flowp)) + "PB"
	case num >= float64(flowt):
		return fmt.Sprintf("%.2f", float64(num)/float64(flowt)) + "TB"
	case num >= float64(flowg):
		return fmt.Sprintf("%.2f", float64(num)/float64(flowg)) + "GB"
	case num >= float64(flowm):
		return fmt.Sprintf("%.2f", float64(num)/float64(flowm)) + "MB"
	case num >= float64(flowk):
		return fmt.Sprintf("%.2f", float64(num)/float64(flowk)) + "KB"
	default:
		return fmt.Sprintf("%.2f", num) + "B"
	}

}
